package com.evil.application.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evil.account.api.RemoteUserEnterpriseService;
import com.evil.account.api.RemoteUserService;
import com.evil.application.api.RemoteApplicationOrderService;
import com.evil.application.enums.form.control.ControlCodeEnum;
import com.evil.application.enums.order.OrderQueryTypeEnum;
import com.evil.application.enums.order.OrderSpecialControlEnum;
import com.evil.application.mapper.*;
import com.evil.application.pojo.dto.button.ButtonInfoRespDTO;
import com.evil.application.pojo.dto.form.control.ControlBaseInfoDTO;
import com.evil.application.pojo.dto.form.control.code.ControlDTO;
import com.evil.application.pojo.dto.form.control.option.FormOptionRespDTO;
import com.evil.application.pojo.dto.order.*;
import com.evil.application.pojo.dto.order.data.OrderRelatedDataMapReqDTO;
import com.evil.application.pojo.dto.order.data.TableRelatedDataPageReqDTO;
import com.evil.application.pojo.dto.order.node.AddOrderNodeBaseReqDTO;
import com.evil.application.pojo.dto.order.node.AuditNodeStatisticsRespDTO;
import com.evil.application.pojo.dto.order.node.HandleOrderNodeReqDTO;
import com.evil.application.pojo.dto.order.node.OrderProcessStatisticsBO;
import com.evil.application.pojo.dto.permission.control.ControlPermissionDTO;
import com.evil.application.pojo.dto.process.node.NodeControlReqDTO;
import com.evil.application.pojo.dto.process.node.NodeControlRespDTO;
import com.evil.application.pojo.entity.*;
import com.evil.application.service.*;
import com.evil.application.util.ApplicationUtil;
import com.evil.application.util.OrderDataUtil;
import com.evil.application.util.RobotUtil;
import com.evil.common.account.dto.user.UserEnterpriseDTO;
import com.evil.common.account.util.LoginUtil;
import com.evil.common.activiti.enums.HandleTypeEnum;
import com.evil.common.application.dto.button.ButtonSourceDTO;
import com.evil.common.application.dto.form.SpecialControlConfigDTO;
import com.evil.common.application.dto.order.OrderPageDataRespDTO;
import com.evil.common.application.dto.order.data.ControlDataDTO;
import com.evil.common.application.dto.order.data.ControlDataValueDTO;
import com.evil.common.application.dto.order.data.SpecialControlData;
import com.evil.common.application.dto.process.ProcessCreateTaskRespDTO;
import com.evil.common.application.dto.process.ProcessReassignmentTaskReqDTO;
import com.evil.common.application.dto.process.node.NodeBaseDTO;
import com.evil.common.application.dto.process.node.btn.NodeButtonConfigDTO;
import com.evil.common.application.dto.process.node.setting.NodeControlSettingDTO;
import com.evil.common.application.enums.ApplicationTypeEnum;
import com.evil.common.application.enums.ApplicationVersionEnum;
import com.evil.common.application.enums.button.ButtonSourceTypeEnum;
import com.evil.common.application.enums.order.OrderProcessStatusEnum;
import com.evil.common.application.enums.order.OrderSourceTypeEnum;
import com.evil.common.application.enums.permission.ControlVisibleEnum;
import com.evil.common.application.util.ApplicationRedisKeyUtil;
import com.evil.common.core.constant.RedisConstant;
import com.evil.common.core.entity.QueryModel;
import com.evil.common.core.enums.RCodeEnum;
import com.evil.common.core.enums.SwitchEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.util.CacheUtil;
import com.evil.common.core.util.RedisUtil;
import com.evil.common.core.util.StreamUtil;
import com.evil.common.log.function.ExecuteTimeFunction;
import com.github.pagehelper.PageInfo;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 应用订单表
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Slf4j
@RestController
@AllArgsConstructor
public class ApplicationOrderServiceImpl extends ServiceImpl<ApplicationOrderMapper, ApplicationOrder> implements ApplicationOrderService, RemoteApplicationOrderService {

    private final LoginUtil loginUtil;

    private final RedisUtil redisUtil;

    private final CacheUtil cacheUtil;

    private final RobotUtil robotUtil;

    private final ApplicationInfoMapper applicationInfoMapper;

    private final ApplicationFormMapper applicationFormMapper;

    private final ApplicationProcessMapper applicationProcessMapper;

    private final ApplicationProcessNodeMapper applicationProcessNodeMapper;

    private final ApplicationInfoService applicationService;

    private final ApplicationFormService applicationFormService;

    private final ApplicationFormControlService applicationFormControlService;

    private final ApplicationFormSnRuleService applicationFormSnRuleService;

    private final FormControlOptionService formControlOptionService;

    private final ApplicationProcessService applicationProcessService;

    private final ApplicationProcessNodeService applicationProcessNodeService;

    private final ControlPermissionService controlPermissionService;

    private final ApplicationApplySettingService applicationApplySettingService;

    private final ApplicationButtonService applicationButtonService;

    private final ApplicationOrderDataService applicationOrderDataService;

    private final ApplicationOrderNodeService applicationOrderNodeService;

    private final ApplicationOrderNodeHandlerService applicationOrderNodeHandlerService;

    private final ElasticsearchService elasticsearchService;

    private final RemoteUserService remoteUserService;

    private final RemoteUserEnterpriseService remoteUserEnterpriseService;

    private final ApplicationLogServiceImpl applicationLogService;

    /**
     * 保存订单列表
     *
     * @param applicationOrders applicationOrders
     */
    @Override
    public void saveOrders(Collection<ApplicationOrder> applicationOrders) {
        applicationLogService.saveBatchAndCheck(this.baseMapper, ApplicationOrder::getOrderId, applicationOrders);
    }

    /**
     * 获取应用订单可见控件
     *
     * @param orderBaseReqDTO orderPageBaseReqDTO
     * @return List
     */
    @Override
    public List<ControlBaseInfoDTO> ordersVisibleControlsBase(OrderBaseReqDTO orderBaseReqDTO) {
        // 获取查询类型
        OrderQueryTypeEnum queryTypeEnum = OrderQueryTypeEnum.findById(orderBaseReqDTO.getQueryType());

        Long loginUserId = loginUtil.getLoginUserId();
        // 应用信息
        ApplicationInfo applicationInfo = applicationInfoMapper.findByKey(orderBaseReqDTO.getApplicationKey());
        // 检查企业
        loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());

        // 获取发布版表单
        ApplicationForm form = applicationFormService.releaseForm(applicationInfo);

        orderBaseReqDTO.setApplicationId(applicationInfo.getApplicationId());
        boolean filterNodeVisibleControl = true;
        switch (queryTypeEnum) {
            case PENDING:
                // 待办事项
                orderBaseReqDTO.addNodeHandlers(HandleTypeEnum.PROCESSING, loginUserId);
                break;
            case HANDLED:
                // 已办事项
                orderBaseReqDTO.addNodeHandlers(loginUserId, HandleTypeEnum.getHandledType());
                break;
            case SEND:
                // 抄送事项
                orderBaseReqDTO.addNodeHandlers(HandleTypeEnum.CARBON, loginUserId);
                break;
            case START:
                // 我发起的
                orderBaseReqDTO.setCreatorId(loginUserId);
                break;
            case DRAFT_BOX:
                orderBaseReqDTO.setProcessStatus(OrderProcessStatusEnum.DRAFT_BOX.getId());
                orderBaseReqDTO.setCreatorId(loginUserId);
                break;
            case ALL:
                // 全部 不查询草稿
                orderBaseReqDTO.setNotEqProcessStatus(OrderProcessStatusEnum.DRAFT_BOX.getId());
                filterNodeVisibleControl = false;
                break;
            default:
                throw new BusinessException(RCodeEnum.ERROR_RUNTIME);
        }

        // 获取当前类型所有订单涉及到的所有节点
        List<ApplicationProcessNode> nodes = applicationProcessNodeMapper.findOrderNodes(orderBaseReqDTO);

        // 获取控件
        List<ControlBaseInfoDTO> controlBases = applicationFormControlService.findBaseInfoByForm(form, true);

        // 默认 需要过滤 订单列表
        Predicate<ControlBaseInfoDTO> filter = e -> ControlCodeEnum.orderListVisible(e.getControlCode());
        // 控件权限
        if (filterNodeVisibleControl && CollectionUtil.isNotEmpty(nodes)) {
            Set<Long> nodeIds = StreamUtil.transSetT(nodes, ApplicationProcessNode::getNodeId);
            Set<Long> controlIds = StreamUtil.transSetT(controlBases, ControlBaseInfoDTO::getControlId);
            // 节点控件map
            Map<Long, Set<Long>> nodeControlIdsMap = nodeIds.stream().collect(Collectors.toMap(e -> e, e -> controlIds));
            // 获取节点控件控件配置列表
            List<Long> visibleControlIds = controlPermissionService.nodesControlPermissions(nodeIds, nodeControlIdsMap).stream()
                    // 过滤 隐藏
                    .filter(e -> ControlVisibleEnum.INVISIBLE.getId() != e.getVisible())
                    .map(ControlPermissionDTO::getControlId)
                    .collect(Collectors.toList());

            // 如果表格的子控件可以看见，则表格可见
            visibleControlIds.addAll(controlBases.stream()
                    // 有上级控件id，并且上级控件不在可见列表（controlIds）
                    .filter(e -> ApplicationUtil.DEFAULT_PARENT_CONTROL_ID.equals(e.getControlId()) && !visibleControlIds.contains(e.getParentControlId()))
                    .map(ControlBaseInfoDTO::getParentControlId)
                    .collect(Collectors.toList()));

            filter = filter.and(e -> visibleControlIds.contains(e.getControlId()));
        }
        // 过滤
        controlBases = StreamUtil.filter(controlBases, filter);
        return ApplicationUtil.handleControlBases(controlBases, nodes);
    }

    /**
     * 获取节点控件
     *
     * @param nodeControlReqDTO nodeControlReqDTO
     * @return NodeControlRespDTO
     */
    @Override
    public NodeControlRespDTO nodeControl(NodeControlReqDTO nodeControlReqDTO) {
        String applicationKey = nodeControlReqDTO.getApplicationKey();
        Long nodeId = nodeControlReqDTO.getNodeId();
        boolean create = nodeControlReqDTO.getCreate();

        // 获取应用
        ApplicationInfo applicationInfo = applicationInfoMapper.findByKey(applicationKey);
        // 检查企业
        loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());

        ApplicationVersionEnum versionEnum = ApplicationVersionEnum.RELEASE_VERSION;
        // 获取应用流程
        ApplicationProcess applicationProcess = applicationProcessMapper.findByAppIdAndVersion(
                applicationInfo.getApplicationId(), versionEnum.getId());
        // 获取应用表单
        ApplicationForm form = applicationFormMapper.findByAppIdAndVersion(
                applicationInfo.getApplicationId(), versionEnum.getId());

        // 获取应用流程节点
        ApplicationProcessNode processNode = null;
        if (null == nodeId) {
            if (create) {
                // 如果没有指定节点，则获取开始节点
                processNode = applicationProcessNodeMapper.isExistStartNodeByProcessId(applicationProcess.getProcessId())
                        .orElseThrow(() -> new BusinessException(RCodeEnum.APPLICATION_PROCESS_START_NODE_NOE_EXIST));
            }
        } else {
            processNode = applicationProcessNodeMapper.findById(nodeId, true);
            // 检查节点与 流程关系
            if (!processNode.getProcessId().equals(applicationProcess.getProcessId())) {
                throw new BusinessException(RCodeEnum.ERROR_PARAMETER);
            }
        }

        // 获取表单控件列表
        List<ControlDTO> controls = applicationFormControlService.findDTOByForm(form, false);

        List<NodeControlSettingDTO> nodeControlSettings;
        if (Objects.nonNull(processNode)) {
            Long currentNodeId = processNode.getNodeId();
            List<Long> controlIds = StreamUtil.transListT(controls, ControlDTO::getControlId);
            // 获取节点控件配置
            nodeControlSettings = controlPermissionService.nodeControlPermissions(currentNodeId, controlIds).stream()
                    .filter(e -> ControlVisibleEnum.INVISIBLE.getId() != e.getVisible())
                    .map(e -> {
                        NodeControlSettingDTO nodeControlSettingDTO = BeanUtil.copyProperties(e, NodeControlSettingDTO.class);
                        nodeControlSettingDTO.setNodeId(currentNodeId);
                        return nodeControlSettingDTO;
                    })
                    .collect(Collectors.toList());
            // 过滤 可见控件
            List<Long> visibleControlIds = StreamUtil.transListT(nodeControlSettings, NodeControlSettingDTO::getControlId);
            controls = StreamUtil.filter(controls, e -> visibleControlIds.contains(e.getControlId()));
        } else {
            nodeControlSettings = StreamUtil.transListT(controls, c -> new NodeControlSettingDTO(c.getControlId(), c.getControlTitle()));
        }

        // 层级分类
        controls = ApplicationUtil.classifyControl(controls);

        // 获取特殊控件
        SpecialControlConfigDTO sourceFormConfig = BeanUtil.copyProperties(form.getSpecialControlConfig(), SpecialControlConfigDTO.class);
        List<ControlBaseInfoDTO> specialControls = OrderSpecialControlEnum.initControlBases(sourceFormConfig);

        NodeControlRespDTO nodeControlRespDTO = new NodeControlRespDTO();
        nodeControlRespDTO.setApplicationFormId(form.getFormId());
        nodeControlRespDTO.setApplicationKey(applicationKey);
        nodeControlRespDTO.setControlsData(controls);
        nodeControlRespDTO.setNodeControlSettings(nodeControlSettings);
        nodeControlRespDTO.setSpecialControls(specialControls);
        return nodeControlRespDTO;
    }

    /**
     * 提交应用订单
     *
     * @param pushOrderReqDTO addOrderReqDTO
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public AddOrderRespDTO pushOrder(PushOrderReqDTO pushOrderReqDTO) {
        AddOrderReqDTO addOrderReqDTO = BeanUtil.copyProperties(pushOrderReqDTO, AddOrderReqDTO.class);
        addOrderReqDTO.setOrderSourceType(OrderSourceTypeEnum.NEW_ORDER.getId());
        if (pushOrderReqDTO.getIsDraftBox()) {
            addOrderReqDTO.setOrderProcessStatus(OrderProcessStatusEnum.DRAFT_BOX.getId());
        } else {
            addOrderReqDTO.setOrderProcessStatus(OrderProcessStatusEnum.PROCESSING.getId());
        }
        return this.addOrder(addOrderReqDTO);
    }

    /**
     * 新增应用订单
     *
     * @param addOrderReqDTO addOrderReqDTO
     * @return AddOrderRespDTO
     */
    @Override
    public AddOrderRespDTO addOrder(AddOrderReqDTO addOrderReqDTO) {
        String applicationKey = addOrderReqDTO.getApplicationKey();
        Long orderId = addOrderReqDTO.getOrderId();
        Integer orderSourceType = addOrderReqDTO.getOrderSourceType();
        Integer orderProcessStatus = addOrderReqDTO.getOrderProcessStatus();
        List<ControlDataDTO> data = addOrderReqDTO.getData();
        Boolean afterRobot = addOrderReqDTO.getAfterRobot();
        UserEnterpriseDTO handler = ObjectUtil.defaultIfNull(addOrderReqDTO.getHandler(), loginUtil.getLoginUserEnterprise());

        OrderSourceTypeEnum orderSourceTypeEnum = OrderSourceTypeEnum.findById(orderSourceType);
        OrderProcessStatusEnum orderProcessStatusEnum = OrderProcessStatusEnum.findById(orderProcessStatus);

        // 获取应用信息
        ApplicationInfo app = applicationInfoMapper.findByKey(applicationKey);
        // 检查企业
        loginUtil.checkEnterpriseId(app.getEnterpriseId());

        // 获取发布版表单
        ApplicationForm form = applicationFormService.releaseForm(app);

        // 获取流程开始节点
        ApplicationProcessNode startNode = applicationProcessNodeService.findStartNode(app.getApplicationId());

        // 检查申请状态
        applicationService.checkApplyApplication(app);

        // 验证表单数据
        if (!orderSourceTypeEnum.isRobot()) {
            applicationOrderDataService.verifyOrderDataList(app, new ArrayList<>(), data, startNode.getNodeId());
        }

        ApplicationOrder applicationOrder;
        if (null != orderId) {
            // 草稿重新发单
            applicationOrder = this.baseMapper.findById(orderId);
            if (OrderProcessStatusEnum.DRAFT_BOX.getId() != applicationOrder.getProcessStatus()) {
                throw new BusinessException(RCodeEnum.APPLICATION_ORDER_IS_SUBMIT);
            }
        } else {
            // 新下单
            applicationOrder = new ApplicationOrder();
        }
        applicationOrder.setEnterpriseId(app.getEnterpriseId());
        applicationOrder.setApplicationId(app.getApplicationId());
        applicationOrder.setProcessStatus(orderProcessStatusEnum.getId());
        applicationOrder.setOrderSourceType(orderSourceTypeEnum.getId());
        applicationOrder.setIsDeleted(SwitchEnum.NO.getId());

        // 订单状态：非草稿 检查申请配置、生成编号
        if (OrderProcessStatusEnum.DRAFT_BOX != orderProcessStatusEnum) {
            // 检查申请配置
            applicationApplySettingService.checkApplicationApplySetting(app);
            // 生成编号
            applicationOrder.setOrderSn(applicationFormSnRuleService.generateSn(form, data));

            applicationOrder.setProcessInstanceId(UUID.randomUUID().toString(true));
        }
        applicationLogService.saveAndCheck(this.baseMapper, ApplicationOrder::getOrderId, applicationOrder);

        // 补充 开始节点 订单数据
        SpecialControlConfigDTO specialControlConfigDTO = BeanUtil.copyProperties(form.getSpecialControlConfig(), SpecialControlConfigDTO.class);
        NodeBaseDTO nodeBase = BeanUtil.copyProperties(startNode, NodeBaseDTO.class);
        SpecialControlData specialControlData = BeanUtil.copyProperties(applicationOrder, SpecialControlData.class);
        OrderSpecialControlEnum.supplyNodeData(specialControlConfigDTO, specialControlData, data, nodeBase);

        // 订单数据 中的申请人
        applicationOrder.setCreatorId(handler.getUserId());

        // 保存订单数据
        applicationOrderDataService.handleAndSave(app, form, applicationOrder, data, true);
        // 需要重新获取数据（因为需要完善后的数据）
        data = applicationOrderDataService.getOrderControlData(applicationOrder);
        // 保存/添加ES订单
        elasticsearchService.save(app, applicationOrder, data);
        // 新增的订单ID 记录到线程
        redisUtil.sSetAndTime(ApplicationRedisKeyUtil.requestUuidRobotAddOrderId(), RedisConstant.REQUEST_UUID_EXPIRE_IN_SECOND, applicationOrder.getOrderId());

        try {
            // 订单状态：流程中 发起流程
            if (OrderProcessStatusEnum.PROCESSING == orderProcessStatusEnum) {
                if (ApplicationTypeEnum.needProcess(app.getApplicationType())) {
                    log.info("新增应用订单 | 发起流程");
                    cacheUtil.cantUseCache();
                    // 开始节点处理人-发起人
                    AddOrderNodeBaseReqDTO addOrderNodeBaseReqDTO = new AddOrderNodeBaseReqDTO();
                    addOrderNodeBaseReqDTO.setOrderId(applicationOrder.getOrderId());
                    addOrderNodeBaseReqDTO.setNodeId(startNode.getNodeId());
                    addOrderNodeBaseReqDTO.setHandleType(HandleTypeEnum.START.getId());
                    // “1” 是 默认 “发起新的” 的处理人分组key
                    addOrderNodeBaseReqDTO.setGroupAssignees(MapUtil.of("1", Collections.singletonList(handler)));
                    applicationOrderNodeService.addOrderNode(addOrderNodeBaseReqDTO);

                    // 发起流程
                    ProcessCreateTaskRespDTO processCreateTaskRespDTO = applicationProcessService.startProcess(applicationOrder, data);

                    // ###重新查询订单，更新流程实例id
                    applicationOrder = this.baseMapper.findById(applicationOrder.getOrderId());
                    applicationOrder.setProcessInstanceId(processCreateTaskRespDTO.getProcessInstanceId());
                    applicationLogService.saveAndCheck(this.baseMapper, ApplicationOrder::getOrderId, applicationOrder);
                }
            }
        } catch (Exception e) {
            // 删除订单队列
            elasticsearchService.delete(applicationOrder.getOrderId());
            throw e;
        }

        robotUtil.after(addOrderReqDTO, applicationOrder, data);
        if (afterRobot) {
            robotUtil.robotMq();
        }

        addOrderReqDTO.setOrderId(applicationOrder.getOrderId());
        AddOrderRespDTO addOrderRespDTO = new AddOrderRespDTO();
        addOrderRespDTO.setOrderId(applicationOrder.getOrderId());
        addOrderRespDTO.setOrderSn(applicationOrder.getOrderSn());
        // 是否返回数据
        if (addOrderReqDTO.getIsReturnData()) {
            addOrderRespDTO.setData(applicationOrderDataService.getOrderControlData(applicationOrder.getOrderId()));
        }
        return addOrderRespDTO;
    }

    /**
     * 编辑应用订单
     *
     * @param modifyOrderReqDTO modifyOrderReqDTO
     * @return AddOrderRespDTO
     */
    @Override
    public AddOrderRespDTO modifyOrder(ModifyOrderReqDTO modifyOrderReqDTO) {
        String applicationKey = modifyOrderReqDTO.getApplicationKey();
        Long orderId = modifyOrderReqDTO.getOrderId();
        List<ControlDataDTO> data = StreamUtil.filter(modifyOrderReqDTO.getData(), c -> !OrderSpecialControlEnum.isSpecialControlId(c.getControlId()));

        // 获取应用信息
        ExecuteTimeFunction start = ExecuteTimeFunction.start();
        ApplicationInfo applicationInfo = applicationInfoMapper.findByKey(applicationKey);
        start.log("【编辑应用订单-获取应用信息】");
        // 检查企业
        start.reset();
        loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());
        start.log("【编辑应用订单-检查企业】");
        // 获取应用表单
        start.reset();
        ApplicationForm applicationForm = applicationFormService.releaseForm(applicationInfo);
        start.log("【编辑应用订单-获取应用表单】");
        // 获取订单
        start.reset();
        ApplicationOrder order = this.baseMapper.findById(orderId);
        start.log("【编辑应用订单-获取订单】");
        // 获取订单数据
        start.reset();
        List<ApplicationOrderData> orderDataList = applicationOrderDataService.orderDataList(orderId);
        start.log("【编辑应用订单-获取订单数据】");
        // 完善订单数据
        start.reset();
        List<ControlDataDTO> oldData = applicationOrderDataService.getOrderControlData(order, orderDataList, null, true);
        start.log("【编辑应用订单-完善订单数据】");

        // 构建返回体
        AddOrderRespDTO addOrderRespDTO = new AddOrderRespDTO();
        addOrderRespDTO.setOrderId(order.getOrderId());
        addOrderRespDTO.setOrderSn(order.getOrderSn());

        // 检查是否有改动
        start.reset();
        if (!robotUtil.before(applicationKey, order, data, oldData)) {
            start.log("【编辑应用订单-经过检查无改动】");
            return addOrderRespDTO;
        }
        start.log("【编辑应用订单-经过检查有改动】");

        // 验证表单数据
        start.reset();
        applicationOrderDataService.verifyOrderDataList(applicationInfo, oldData, data, null);
        start.log("【编辑应用订单-验证表单数据】");

        // 保存订单
        start.reset();
        applicationLogService.saveAndCheck(this.baseMapper, ApplicationOrder::getOrderId, order);
        start.log("【编辑应用订单-保存订单】");

        // 处理并保存订单数据
        start.reset();
        applicationOrderDataService.handleAndSave(applicationInfo, applicationForm, order, orderDataList, data, false);
        start.log("【编辑应用订单-处理并保存订单数据】");

        // 重新获取订单数据
        start.reset();
        data = applicationOrderDataService.getOrderControlData(order);
        start.log("【编辑应用订单-重新获取订单数据】");

        // ES：更新订单数据
        start.reset();
        elasticsearchService.save(order, OrderDataUtil.getChangeMap(data, oldData));
        start.log("【编辑应用订单-ES：更新订单数据】");

        // 执行所有机器人
        start.reset();
        robotUtil.robotMq();
        start.log("【编辑应用订单-执行所有机器人】");

        // 是否返回数据
        if (modifyOrderReqDTO.getIsReturnData()) {
            addOrderRespDTO.setData(data);
        }
        return addOrderRespDTO;
    }

    /**
     * 批量编辑应用订单 （机器人）
     *
     * @param appOrdersControlDataDTO appOrdersControlDataDTO
     */
    @Override
    public void modifyOrderBatch(AppOrdersControlDataDTO appOrdersControlDataDTO) {
        String applicationKey = appOrdersControlDataDTO.getApplicationKey();
        Map<Long, List<ControlDataDTO>> ordersDataList = appOrdersControlDataDTO.getOrdersDataListMap();

        // 获取应用信息
        ExecuteTimeFunction start = ExecuteTimeFunction.start();
        ApplicationInfo applicationInfo = applicationInfoMapper.findByKey(applicationKey);
        start.log("【批量编辑应用订单（机器人）-获取应用信息】");
        // 检查企业
        start.reset();
        loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());
        start.log("【批量编辑应用订单（机器人）-检查企业】");

        start.reset();
        ApplicationForm applicationForm = applicationFormService.releaseForm(applicationInfo);
        start.log("【批量编辑应用订单（机器人）-获取表单】");

        // 获取订单列表
        start.reset();
        Map<Long, ApplicationOrder> orderMap = this.baseMapper.findByIds(ordersDataList.keySet(), true).stream()
                .collect(Collectors.toMap(ApplicationOrder::getOrderId, e -> e));
        start.log("【批量编辑应用订单（机器人）-获取订单列表】");
        List<Long> unExistList = ordersDataList.keySet().stream().filter(e -> !orderMap.containsKey(e)).collect(Collectors.toList());
        if (!unExistList.isEmpty()) {
            log.error("【批量编辑应用订单（机器人）-订单数据不匹配】unExistList:{}， orderMap-orderIds:{}", unExistList, orderMap.keySet());
            throw new BusinessException(RCodeEnum.APPLICATION_ORDER_NOT_EXIST, unExistList);
        }
        // 获取订单数据列表
        start.reset();
        Map<Long, List<ControlDataDTO>> oldDataMap = applicationOrderDataService.orderListData(orderMap.values(), false, true);
        start.log("【批量编辑应用订单（机器人）-获取订单数据列表】");
        unExistList = ordersDataList.keySet().stream().filter(e -> !oldDataMap.containsKey(e)).collect(Collectors.toList());
        if (!unExistList.isEmpty()) {
            throw new BusinessException(RCodeEnum.APPLICATION_ORDER_DATA_NOT_EXIST, unExistList);
        }

        // 检查是否有改动
        start.reset();
        if (!robotUtil.before(appOrdersControlDataDTO, orderMap, oldDataMap)) {
            start.log("【批量编辑应用订单（机器人）-经过检查无改动】");
            return;
        }
        start.log("【批量编辑应用订单（机器人）-经过检查有改动】");

        start.reset();
        List<ApplicationOrder> modifyOrders = new ArrayList<>();
        List<ApplicationOrderData> modifies = new ArrayList<>();
        Date now = new Date();
        ordersDataList.forEach((orderId, data) -> {
            ApplicationOrder order = orderMap.get(orderId);
            order.setUpdated(now);
            modifyOrders.add(order);
            // 保存订单数据
            start.reset();
            modifies.addAll(applicationOrderDataService.handle(applicationInfo, applicationForm, order, data, false));
            start.log("【批量编辑应用订单（机器人）-处理一个订单数据】");
            // 编辑订单ID 记录到线程
            redisUtil.sSetAndTime(ApplicationRedisKeyUtil.requestUuidRobotModifyOrderId(),
                    RedisConstant.REQUEST_UUID_EXPIRE_IN_SECOND, orderId);
        });
        start.log("【批量编辑应用订单（机器人）-处理全部订单】");

        // 批量保存订单
        start.reset();
        applicationLogService.saveAndCheck(this.baseMapper, ApplicationOrder::getOrderId, modifyOrders);
        start.log("【批量编辑应用订单（机器人）-批量保存订单】");

        // 批量保存订单数据
        start.reset();
        applicationOrderDataService.save(modifies);
        start.log("【批量编辑应用订单（机器人）-批量保存订单数据】");

        // 重新获取数据
        start.reset();
        ordersDataList = applicationOrderDataService.orderListData(orderMap.values(), false, true);
        start.log("【批量编辑应用订单（机器人）-重新获取数据】");

        // ES：更新订单数据
        start.reset();
        elasticsearchService.refreshBatch(orderMap, ordersDataList);
        start.log("【批量编辑应用订单（机器人）：更新订单数据】");

        start.reset();
        robotUtil.robotMq();
        start.log("【批量编辑应用订单（机器人）-执行所有机器人】");
    }

    /**
     * 导入订单
     *
     * @param applicationForm applicationForm
     * @param data            data
     * @return ApplicationOrder
     */
    @Override
    public ApplicationOrder handleImportOrders(ApplicationInfo applicationInfo, ApplicationForm applicationForm, List<ControlDataDTO> data) {
        ApplicationOrder applicationOrder = new ApplicationOrder();
        applicationOrder.setEnterpriseId(applicationInfo.getEnterpriseId());
        applicationOrder.setApplicationId(applicationForm.getApplicationId());
        applicationOrder.setOrderSn(applicationFormSnRuleService.generateSn(applicationForm, data));
        // 标识流程已完成
        applicationOrder.setProcessStatus(OrderProcessStatusEnum.COMPLETED.getId());
        // TODO 不发起流程 但是流程实例需要随机生成
        applicationOrder.setProcessInstanceId(UUID.fastUUID().toString(true));
        applicationOrder.setOrderSourceType(OrderSourceTypeEnum.IMPORT_ORDER.getId());
        applicationOrder.setIsDeleted(SwitchEnum.NO.getId());
        return applicationOrder;
    }

    /**
     * 应用订单流转
     *
     * @param completeOrderReqDTO completeOrderReqDTO
     */
    @Override
    public void completeOrder(CompleteOrderReqDTO completeOrderReqDTO) {
        Long orderId = completeOrderReqDTO.getOrderId();
        Long nodeId = completeOrderReqDTO.getNodeId();
        List<ControlDataDTO> data = completeOrderReqDTO.getData();

        // 获取订单
        ApplicationOrder order = this.baseMapper.findById(orderId);

        // 获取应用信息
        ApplicationInfo app = applicationInfoMapper.findById(order.getApplicationId());
        // 检查应用状态
        applicationService.checkApplication(app);
        // 检查企业
        loginUtil.checkEnterpriseId(app.getEnterpriseId());

        // 获取节点
        applicationProcessNodeMapper.findById(nodeId, false);

        // 检查指定订单当前登录人
        applicationOrderNodeHandlerService.checkLoginUserNodeHandle(orderId, nodeId);

        // 获取发布版表单
        ApplicationForm form = applicationFormService.releaseForm(app);

        // 校验订单状态
        if (OrderProcessStatusEnum.PROCESSING.getId() != order.getProcessStatus()) {
            throw new BusinessException(RCodeEnum.APPLICATION_ORDER_NOT_PROCESSING);
        }

        // 校验订单数据，通过时保存，并且重新获取最新数据
        completeOrderReqDTO.setData(applicationOrderDataService.checkAndSave(app, form, order, data, nodeId));

        // 流转
        applicationProcessService.completeProcess(order, completeOrderReqDTO);
    }

    /**
     * 订单列表
     *
     * @param queryModel queryModel
     */
    @Override
    public PageInfo<OrderPageDataRespDTO> orderList(QueryModel<OrderPageReqDTO> queryModel) {
        Long loginUserId = loginUtil.getLoginUserId();
        OrderPageReqDTO orderBaseReqDTO = queryModel.getCheckParam().orElseThrow(() -> new BusinessException(RCodeEnum.ERROR_PARAMETER));

        // 获取应用
        ApplicationInfo applicationInfo = applicationInfoMapper.findByKey(orderBaseReqDTO.getApplicationKey());
        // 检查企业
        loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());

        // 获取查询类型
        OrderQueryTypeEnum queryTypeEnum = OrderQueryTypeEnum.findById(orderBaseReqDTO.getQueryType());
        switch (queryTypeEnum) {
            case PENDING:
                // 待办事项
                orderBaseReqDTO.addNodeHandlers(HandleTypeEnum.PROCESSING, loginUserId);
                break;
            case HANDLED:
                // 已办事项
                orderBaseReqDTO.addNodeHandlers(loginUserId, HandleTypeEnum.getHandledType());
                break;
            case SEND:
                // 抄送事项
                orderBaseReqDTO.addNodeHandlers(HandleTypeEnum.CARBON, loginUserId);
                break;
            case START:
                // 我发起的
                orderBaseReqDTO.setCreatorId(loginUserId);
                orderBaseReqDTO.setNotEqProcessStatus(OrderProcessStatusEnum.DRAFT_BOX.getId());
                break;
            case DRAFT_BOX:
                // 草稿
                orderBaseReqDTO.setProcessStatus(OrderProcessStatusEnum.DRAFT_BOX.getId());
                orderBaseReqDTO.setCreatorId(loginUserId);
                break;
            case ALL:
                // 全部 不查询草稿
                orderBaseReqDTO.setNotEqProcessStatus(OrderProcessStatusEnum.DRAFT_BOX.getId());
                break;
            default:
                throw new BusinessException(RCodeEnum.ERROR_RUNTIME);
        }
        // 设置查询参数-应用id
        orderBaseReqDTO.setApplicationId(applicationInfo.getApplicationId());

        return elasticsearchService.orderDataPage(queryModel);
    }

    /**
     * 订单统计
     *
     * @param orderStatisticsReqDTO orderStatisticsReqDTO
     * @return List
     */
    @Override
    public List<OrderStatisticsRespDTO> orderStatistics(OrderStatisticsReqDTO orderStatisticsReqDTO) {
        Long loginUserId = loginUtil.getLoginUserId();
        Long enterpriseId = loginUtil.getLoginEnterpriseId();

        Long applicationId = null;
        if (StringUtils.isNotBlank(orderStatisticsReqDTO.getApplicationKey())) {
            ApplicationInfo applicationInfo = applicationInfoMapper.findByKey(orderStatisticsReqDTO.getApplicationKey());
            // 检查企业
            loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());

            applicationId = applicationInfo.getApplicationId();
        }

        Long finalApplicationId = applicationId;
        return OrderQueryTypeEnum.stream(em -> {
            StatisticsProcessOrderDTO statistics = new StatisticsProcessOrderDTO();
            statistics.setEnterpriseId(enterpriseId);
            statistics.setApplicationId(finalApplicationId);
            switch (em) {
                case PENDING:
                    // 待办事项
                    statistics.setHandleType(HandleTypeEnum.PROCESSING.getId());
                    statistics.setHandlers(Collections.singletonList(loginUserId));
                    break;
                case HANDLED:
                    // 已办事项
                    statistics.setHandleTypes(HandleTypeEnum.getHandledType());
                    statistics.setHandlers(Collections.singletonList(loginUserId));
                    break;
                case SEND:
                    // 抄送事项
                    statistics.setHandleType(HandleTypeEnum.CARBON.getId());
                    statistics.setHandlers(Collections.singletonList(loginUserId));
                    break;
                case START:
                    // 我发起的
                    statistics.setHandleType(HandleTypeEnum.START.getId());
                    statistics.setCreatorId(loginUserId);
                    break;
                case DRAFT_BOX:
                    statistics.setProcessStatus(OrderProcessStatusEnum.DRAFT_BOX.getId());
                    statistics.setCreatorId(loginUserId);
                    break;
                case ALL:
                    // 全部
                    break;
                default:
                    throw new BusinessException(RCodeEnum.ERROR_RUNTIME);
            }

            List<OrderProcessStatisticsBO> statisticsList = this.baseMapper.statisticsOrder(statistics);

            // 节点分类
            Map<AuditNodeStatisticsRespDTO, List<OrderProcessStatisticsBO>> statisticsMap = statisticsList.stream()
                    .collect(Collectors.groupingBy(AuditNodeStatisticsRespDTO::new));

            // 节点分类统计
            List<AuditNodeStatisticsRespDTO> nodeStatistics = statisticsMap.entrySet().stream()
                    .map(entry -> entry.getKey().setCount(entry.getValue().size()))
                    .collect(Collectors.toList());

            OrderStatisticsRespDTO orderStatistics = new OrderStatisticsRespDTO();
            orderStatistics.setQueryType(em.getId());
            orderStatistics.setCount(statisticsList.size());
            orderStatistics.setUnread(0);
            orderStatistics.setAuditNodeStatistics(nodeStatistics);

            return orderStatistics;
        }).collect(Collectors.toList());
    }

    /**
     * 订单详情
     *
     * @param orderDetailReqDTO orderDetailReqDTO
     * @return OrderRespDTO
     */
    @Override
    public OrderDetailRespDTO orderDetail(OrderDetailReqDTO orderDetailReqDTO) {
        Long orderId = orderDetailReqDTO.getOrderId();
        Long nodeId = orderDetailReqDTO.getNodeId();

        // 获取订单
        ApplicationOrder order = this.baseMapper.findById(orderId);

        // 获取应用
        ApplicationInfo app = applicationInfoMapper.findById(order.getApplicationId());
        // 检查企业
        loginUtil.checkEnterpriseId(app.getEnterpriseId());

        ApplicationForm form = applicationFormMapper.findByAppIdAndVersion(
                app.getApplicationId(), ApplicationVersionEnum.RELEASE_VERSION.getId());

        // 是否指定节点/否则获取全部数据
        ApplicationProcessNode node = null;
        if (null != nodeId) {
            // 获取节点
            node = applicationProcessNodeMapper.findById(nodeId, false);
        }

        List<ControlDataDTO> orderDataList = applicationOrderDataService.getOrderControlData(order, node, true);

        // 订单特殊控件数据
        Map<Long, List<ControlDataValueDTO>> controlDataMap = StreamUtil.toMapKV(orderDataList, ControlDataDTO::getControlId, ControlDataDTO::getValues);

        SpecialControlConfigDTO specialControlConfigDTO = BeanUtil.copyProperties(form.getSpecialControlConfig(), SpecialControlConfigDTO.class);
        List<ControlDataDTO> controlDataList = OrderSpecialControlEnum.initControlData(specialControlConfigDTO, null, remoteUserService::findBaseById);

        // 没有数据证明无权限查看，此处默认返回给前端 无权查看
        List<ControlDataDTO> specialData = StreamUtil.peek(controlDataList, e -> {
            List<ControlDataValueDTO> values = new ArrayList<>();
            if (controlDataMap.containsKey(e.getControlId())) {
                values = controlDataMap.get(e.getControlId());
            }
            if (values.isEmpty()) {
                // 无权限查看则默认返回无权查看
                String value;
                if (OrderSpecialControlEnum.NODE.getId().equals(e.getControlId())) {
                    value = "流程中";
                } else {
                    value = "无权限查看";
                }
                values.add(new ControlDataValueDTO(value));
            }
            e.setValues(values);
        });

        // 自定义按钮
        List<ButtonInfoRespDTO> buttons = applicationButtonService.findBySource(new ButtonSourceDTO(
                ButtonSourceTypeEnum.APPLICATION_BUTTON, order.getApplicationId(), ApplicationVersionEnum.RELEASE_VERSION.getId()));

        // 返回
        OrderDetailRespDTO orderDetailRespDTO = new OrderDetailRespDTO(order);
        orderDetailRespDTO.setData(orderDataList);
        orderDetailRespDTO.setSpecialData(specialData);
        orderDetailRespDTO.setApplicationKey(app.getApplicationKey());
        orderDetailRespDTO.setApplicationName(app.getApplicationName());
        if (null != node) {
            // 节点配置
            BeanUtil.copyProperties(node, orderDetailRespDTO);
            if (StringUtils.isNotBlank(node.getNodeButtonConfig())) {
                orderDetailRespDTO.setNodeButton(JSON.parseObject(node.getNodeButtonConfig(), NodeButtonConfigDTO.class));
            }
        }
        orderDetailRespDTO.setButtons(buttons);
        return orderDetailRespDTO;
    }

    /**
     * 转交
     *
     * @param orderReassignmentReqDTO orderPassOnReqDTO
     */
    @Override
    public void reassignment(OrderReassignmentReqDTO orderReassignmentReqDTO) {
        Long loginUserId = loginUtil.getLoginUserId();
        Long loginEnterpriseId = loginUtil.getLoginEnterpriseId();
        log.info("【订单转交】，用户：{}，参数：{}", loginUserId, JSON.toJSONString(orderReassignmentReqDTO));
        Long orderId = orderReassignmentReqDTO.getOrderId();
        Long nodeId = orderReassignmentReqDTO.getNodeId();
        Long assignee = orderReassignmentReqDTO.getAssignee();
        String remark = orderReassignmentReqDTO.getRemark();

        // 获取订单
        ApplicationOrder applicationOrder = this.baseMapper.findById(orderId);
        // 获取应用信息
        ApplicationInfo applicationInfo = applicationInfoMapper.findById(applicationOrder.getApplicationId());
        // 检查应用状态
        applicationService.checkApplication(applicationInfo);
        // 检查企业
        loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());

        if (OrderProcessStatusEnum.PROCESSING.getId() != applicationOrder.getProcessStatus()) {
            throw new BusinessException(RCodeEnum.APPLICATION_ORDER_NOT_PROCESSING);
        }

        // 检查节点
        ApplicationProcessNode processNode = applicationProcessNodeMapper.findById(nodeId, false);

        // 校验用户是否在当前企业
        remoteUserService.findNormalBaseById(assignee);
        UserEnterpriseDTO userEnterpriseDTO = new UserEnterpriseDTO(loginEnterpriseId, assignee);
        remoteUserEnterpriseService.checkEnterpriseUser(userEnterpriseDTO);

        // 订单节点流转
        HandleOrderNodeReqDTO handleOrderNodeReqDTO = new HandleOrderNodeReqDTO();
        handleOrderNodeReqDTO.setOrderId(orderId);
        handleOrderNodeReqDTO.setNodeId(nodeId);
        handleOrderNodeReqDTO.setHandlerId(assignee);
        handleOrderNodeReqDTO.setRemark(remark);
        handleOrderNodeReqDTO.setHandleType(HandleTypeEnum.REASSIGNMENT.getId());
        applicationOrderNodeService.handleOrderNode(handleOrderNodeReqDTO);

        // 工作流转交
        ProcessReassignmentTaskReqDTO processReassignmentTaskReqDTO = new ProcessReassignmentTaskReqDTO();
        processReassignmentTaskReqDTO.setProcessInstanceId(applicationOrder.getProcessInstanceId());
        processReassignmentTaskReqDTO.setNodeBaseDTO(new NodeBaseDTO(processNode.getNodeId(), processNode.getNodeType()));
        processReassignmentTaskReqDTO.setUserEnterpriseDTO(userEnterpriseDTO);
        applicationProcessService.reassignment(processReassignmentTaskReqDTO);
    }

    /**
     * 删除订单
     *
     * @param deleteOrderReqDTO deleteOrderReqDTO
     */
    @Override
    public void deleteOrder(DeleteOrderReqDTO deleteOrderReqDTO) {
        String applicationKey = deleteOrderReqDTO.getApplicationKey();
        Boolean deletedAll = deleteOrderReqDTO.getDeletedAll();
        List<Long> orderIds = deleteOrderReqDTO.getOrderIds();
        Long enterpriseId = loginUtil.getParamEnterpriseId(deleteOrderReqDTO.getEnterpriseId());

        ApplicationInfo applicationInfo = applicationInfoMapper.findByKey(applicationKey);
        loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());

        List<Long> deletedIds;
        List<ApplicationOrder> orders;
        // 删除全部
        if (deletedAll) {
            // 获取搜索引擎已有订单ids
            deletedIds = elasticsearchService.orderIds(deleteOrderReqDTO);
            if (deletedIds.isEmpty()) {
                return;
            }
            orders = this.baseMapper.findByIds(deletedIds, true);
        }
        // 删除指定
        else {
            if (deleteOrderReqDTO.getOrderIds().isEmpty()) {
                return;
            }
            orders = this.baseMapper.findByIds(orderIds, true);
            if (orderIds.size() != orders.size()) {
                throw new BusinessException(RCodeEnum.APPLICATION_ORDER_NOT_EXIST);
            }
            deletedIds = orders.stream().map(ApplicationOrder::getOrderId).collect(Collectors.toList());
        }

        // 不允许删除其他企业订单
        if (orders.stream().anyMatch(e -> !enterpriseId.equals(e.getEnterpriseId()))) {
            throw new BusinessException(RCodeEnum.ACCESS_DENIED);
        }
        orders.forEach(e -> e.setIsDeleted(SwitchEnum.YES.getId()));
        applicationLogService.saveAndCheck(this.baseMapper, ApplicationOrder::getOrderId, orders);

        // 删除订单数据
        List<ApplicationOrderData> orderDataList = applicationOrderDataService.ordersDataList(orderIds, true);
        orderDataList.forEach(e -> e.setIsDeleted(SwitchEnum.YES.getId()));
        applicationOrderDataService.save(orderDataList);

        // 删除ES订单
//        redisUtil.lpushAll(RedisKeyUtil.delESOrderMQKey(), orderIds);
        elasticsearchService.delete(deletedIds);
    }

    /**
     * 初始化应用订单数据（机器人创建订单）
     *
     * @param applicationId applicationId
     * @return InitControlDataDTO
     */
    @Override
    public InitControlDataRespDTO initAppOrderData(Long applicationId) {
        // 应用
        ExecuteTimeFunction start = ExecuteTimeFunction.start();
        ApplicationInfo applicationInfo = applicationInfoMapper.findById(applicationId);
        start.log("【初始化应用订单数据（机器人创建订单）- 获取应用】");
        // 应用表单控件 数据
        start.reset();
        List<ControlDTO> controls = applicationFormControlService.appReleaseControlDTOs_Cache(applicationInfo);
        start.log("【初始化应用订单数据（机器人创建订单）- 应用表单控件】");
        // 层级分类 （###不需要覆盖返回值）
        ApplicationUtil.classifyControl(controls);

        List<Long> controlIds = new ArrayList<>();
        Map<Long, ControlDTO> controlMap = new HashMap<>();
        Map<Long, ControlDataDTO> controlDataMap = new HashMap<>();
        controls.forEach(control -> {
            controlIds.add(control.getControlId());
            controlMap.put(control.getControlId(), control);
            controlDataMap.put(control.getControlId(), ApplicationUtil.controlToControlData(control));
        });
        start.reset();
        Map<Long, List<FormOptionRespDTO>> controlOptionsMap = formControlOptionService.findBasesByControlIds(controlIds, true);
        start.log("【初始化应用订单数据（机器人创建订单）- 获取控件选项】");

        InitControlDataRespDTO initControlDataRespDTO = new InitControlDataRespDTO();
        initControlDataRespDTO.setApplicationKey(applicationInfo.getApplicationKey());
        initControlDataRespDTO.setControlDataMap(controlDataMap);
        initControlDataRespDTO.setControlMap(controlMap);
        initControlDataRespDTO.setControlOptionsMap(controlOptionsMap);
        return initControlDataRespDTO;
    }

    /**
     * 获取订单指定控件数据map
     *
     * @param queryModel queryModel
     * @return Map
     */
    @Override
    public Map<String, List<ControlDataDTO>> orderRelatedDataMap(QueryModel<OrderRelatedDataMapReqDTO> queryModel) {
        return null;
    }

    /**
     * 表格关联数据page
     *
     * @param tableRelatedDataPageReqDTO tableRelatedDataPageReqDTO
     * @return PageInfo
     */
    @Override
    public PageInfo<OrderPageDataRespDTO> tableRelatedDataPage(TableRelatedDataPageReqDTO tableRelatedDataPageReqDTO) {
        return null;
    }
}

